#ifndef _ZUCLOG_QUE_H_
#define _ZUCLOG_QUE_H_

#include <cstdint>
#include <mutex>

template <typename T, bool ThreadSafe> class SimpleQueue
{
public:
    SimpleQueue(int32_t size);
    ~SimpleQueue();

private:
    SimpleQueue(const SimpleQueue&) = delete;
    SimpleQueue& operator=(const SimpleQueue&) = delete;

public:
    SimpleQueue(SimpleQueue&& other);
    SimpleQueue& operator=(SimpleQueue&& other);

public:
    int32_t Push(const T& item);
    int32_t Pop(T& item);

private:
    bool Empty() const;
    bool Full() const;
    void Resize(int size);

private:
    std::mutex mtx;
    int32_t maxSize;
    int32_t frontIndex;
    int32_t rearIndex;
    int32_t size;
    T* items;
};

template <typename T, bool ThreadSafe>
SimpleQueue<T, ThreadSafe>::SimpleQueue(int32_t size) : maxSize(std::max(size, 0)), frontIndex(-1), rearIndex(-1), size(0), items(new T[maxSize])
{
}

template <typename T, bool ThreadSafe> SimpleQueue<T, ThreadSafe>::~SimpleQueue()
{
    if (items)
    {
        delete[] items;
        items = nullptr;
    }
}

template <typename T, bool ThreadSafe> SimpleQueue<T, ThreadSafe>::SimpleQueue(SimpleQueue&& other)
{
    std::lock_guard<std::mutex> lk(other.mtx);
    this->maxSize = other.maxSize;
    this->frontIndex = other.frontIndex;
    this->rearIndex = other.rearIndex;
    this->size = other.size;
    this->items = other.items;
    other.frontIndex = -1;
    other.rearIndex = -1;
    other.size = 0;
    other.items = nullptr;
}

template <typename T, bool ThreadSafe> SimpleQueue<T, ThreadSafe>& SimpleQueue<T, ThreadSafe>::operator=(SimpleQueue&& other)
{
    std::lock_guard<std::mutex> lk_other(other.mtx);
    std::lock_guard<std::mutex> lk(this->mtx);

    if (this->items)
    {
        delete[] this->items;
        this->items = nullptr;
    }

    this->maxSize = other.maxSize;
    this->frontIndex = other.frontIndex;
    this->rearIndex = other.rearIndex;
    this->size = other.size;
    this->items = other.items;
    other.frontIndex = -1;
    other.rearIndex = -1;
    other.size = 0;
    other.items = nullptr;
}

template <typename T, bool ThreadSafe> int32_t SimpleQueue<T, ThreadSafe>::Push(const T& item)
{
    std::lock_guard<std::mutex> lk(mtx);

    if (Full())
    {
        return -1;
    }

    if (frontIndex == -1)
    {
        frontIndex = 0;
    }

    rearIndex = (rearIndex + 1) % maxSize;
    items[rearIndex] = item;
    size += 1;

    return 0;
}

template <typename T, bool ThreadSafe> int32_t SimpleQueue<T, ThreadSafe>::Pop(T& item)
{
    std::lock_guard<std::mutex> lk(mtx);

    if (Empty())
    {
        return -1;
    }

    item = items[frontIndex];
    if (frontIndex == rearIndex)
    {
        frontIndex = -1;
        rearIndex = -1;
    }
    else
    {
        frontIndex = (frontIndex + 1) % maxSize;
    }
    size -= 1;
    return 0;
}

template <typename T, bool ThreadSafe> bool SimpleQueue<T, ThreadSafe>::Empty() const { return size == 0; }

template <typename T, bool ThreadSafe> bool SimpleQueue<T, ThreadSafe>::Full() const { return size == maxSize; }

template <typename T> class SimpleQueue<T, false>
{
public:
    SimpleQueue(int32_t size);
    ~SimpleQueue();

private:
    SimpleQueue(const SimpleQueue&) = delete;
    SimpleQueue& operator=(const SimpleQueue&) = delete;

public:
    SimpleQueue(SimpleQueue&& other);
    SimpleQueue& operator=(SimpleQueue&& other);

public:
    int32_t Push(const T& item);
    int32_t Pop(T& item);

    bool Empty() const;
    bool Full() const;

private:
    void Resize(int size);

private:
    int32_t maxSize;
    int32_t frontIndex;
    int32_t rearIndex;
    int32_t size;
    T* items;
};

template <typename T>
SimpleQueue<T, false>::SimpleQueue(int32_t size) : maxSize(std::max(size, 0)), frontIndex(-1), rearIndex(-1), size(0), items(new T[maxSize])
{
}

template <typename T> SimpleQueue<T, false>::~SimpleQueue()
{
    if (items)
    {
        delete[] items;
        items = nullptr;
    }
}

template <typename T> SimpleQueue<T, false>::SimpleQueue(SimpleQueue&& other)
{
    this->maxSize = other.maxSize;
    this->frontIndex = other.frontIndex;
    this->rearIndex = other.rearIndex;
    this->size = other.size;
    this->items = other.items;
    other.frontIndex = -1;
    other.rearIndex = -1;
    other.size = 0;
    other.items = nullptr;
}

template <typename T> SimpleQueue<T, false>& SimpleQueue<T, false>::operator=(SimpleQueue&& other)
{
    if (this->items)
    {
        delete[] this->items;
        this->items = nullptr;
    }

    this->maxSize = other.maxSize;
    this->frontIndex = other.frontIndex;
    this->rearIndex = other.rearIndex;
    this->size = other.size;
    this->items = other.items;
    other.frontIndex = -1;
    other.rearIndex = -1;
    other.size = 0;
    other.items = nullptr;
    return *this;
}

template <typename T> int32_t SimpleQueue<T, false>::Push(const T& item)
{
    if (Full())
    {
        return -1;
    }

    if (frontIndex == -1)
    {
        frontIndex = 0;
    }

    rearIndex = (rearIndex + 1) % maxSize;
    items[rearIndex] = item;
    size += 1;

    return 0;
}

template <typename T> int32_t SimpleQueue<T, false>::Pop(T& item)
{
    if (Empty())
    {
        return -1;
    }

    item = items[frontIndex];
    if (frontIndex == rearIndex)
    {
        frontIndex = -1;
        rearIndex = -1;
    }
    else
    {
        frontIndex = (frontIndex + 1) % maxSize;
    }
    size -= 1;
    return 0;
}

template <typename T> bool SimpleQueue<T, false>::Empty() const { return size == 0; }

template <typename T> bool SimpleQueue<T, false>::Full() const { return size == maxSize; }

#endif
